using System;
using System.Collections.Generic;
using System.Text;
using agsXMPP;
using agsXMPP.Xml.Dom;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;


namespace SwinMessengerLibrary
{
    public class Server
    {
        private String _Hostname;

        public String Hostname
        {
            get { return _Hostname; }
            set { _Hostname = value; }
        }
        private string _Username;

        public string Username
        {
            get { return _Username; }
            set { _Username = value; }
        }
        private string _Password;

        public string Password
        {
            get { return _Password; }
            set { _Password = value; }
        }
        private Roster _Roster;
        public Roster Roster
        {
            get { return _Roster; }
        }
        private bool _CanLogin;

        public bool CanLogin
        {
            get { return _CanLogin; }
        }
        private XmppClientConnection _Xmpp;

        /// <summary>
        /// This event is trigger by the XMPPClientConnection object's OnLogin event when 
        /// the connected server has approved the user login.
        /// </summary>
        public event EventHandler OnLogin;

        /// <summary>
        /// Informs all the handlers, such as a user interface, whether the closing process 
        /// is approved. This event, will pass an empty EventArgs object to all registered 
        /// handlers. It will pass a CantCloseEventArgs object when at least one of the chat
        /// session is still active.
        /// </summary>
        public event EventHandler OnClose;

        /// <summary>
        /// Informs all the event handlers that the authencation is unsuccessful.
        /// </summary>
        public event EventHandler OnAuthError;

        public Server()
        {
            _Roster = new Roster(this);

            _CanLogin = true;
        }

        /// <summary>
        /// The constructor is used to create a new server object.
        /// </summary>
        /// <param name="hostname">the server domain name or IP address</param>
        /// <param name="username">the username</param>
        /// <param name="password">the user's password</param>
        public Server(string hostname, string username, string password)
            : this()
        {
            _Hostname = hostname;
            _Username = username;
            _Password = password;
        }

        /// <summary>
        /// Open the connection to the IM server.
        /// </summary>
        public void Login()
        {
            _Xmpp = new XmppClientConnection(_Hostname);
            _Xmpp.Username = _Username;
            _Xmpp.Password = _Password;
            _Xmpp.AutoResolveConnectServer = true;

            // registering the event handlers to the XmppClientConnection object
            _Xmpp.OnLogin += new ObjectHandler(OnLoginHandler);
            _Xmpp.OnAuthError += new XmppElementHandler(OnAuthErrorHandler);
            _Xmpp.OnClose += new ObjectHandler(OnCloseHandler);
            _Xmpp.OnPresence += new PresenceHandler(OnPresenceHandler);
            _Xmpp.OnRosterItem += new agsXMPP.XmppClientConnection.RosterHandler(OnRosterItemHandler);
            _Xmpp.OnMessage += new MessageHandler(OnMessageHandler);

            _Xmpp.Open();
        }

        /// <summary>
        /// Close the IM server connection.
        /// </summary>
        /// <returns>whether the log out process is successful.</returns>
        public bool Logout(bool forced)
        {
            if (forced || _Roster.Closing())
            {
                _Roster.Close();
                _Xmpp.Close();
                _CanLogin = true;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Close the IM server connection and trigger the OnClose event.
        /// </summary>
        /// <param name="forced"></param>
        /// <returns></returns>
        public bool Close(bool forced)
        {
            if (forced || Logout(false))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// This handler just informs the interface that the previous authentication failed.
        /// The interface should then allow the user to try again.
        /// </summary>
        /// <param name="sender">the object that triggered this event</param>
        /// <param name="e">the reason of failure</param>
        private void OnAuthErrorHandler(object sender, Element e)
        {
            _CanLogin = true;
            if (OnAuthError != null)
            {
                OnAuthError(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Sends the presence back to the server. Then informs the interface that the
        /// authentication succeeded. The interface then should show the roster and wait for
        /// the roster item update.
        /// </summary>
        /// <param name="sender">the object that triggered this event</param>
        private void OnLoginHandler(object sender)
        {
            _Xmpp.SendMyPresence();
            _CanLogin = false;
            if (OnLogin != null)
            {
                OnLogin(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the close event from the XmppClientConnection object.
        /// </summary>
        /// <param name="sender">the object that triggered this event</param>
        private void OnCloseHandler(object sender)
        {
            Close(true);
            if (OnClose != null)
            {
                OnClose(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Handles the incoming messages. This handler should call the roster's Chat method.
        /// </summary>
        /// <param name="sender">the object that triggered this event.</param>
        /// <param name="eventArgs">contains the message object</param>
        private void OnMessageHandler(object sender, Message msg)
        {
            string state = msg.Chatstate.ToString();
            string id = msg.From.ToString();
            string[] ids = id.Split('/');
            string message = msg.Body;
            _Roster.Chat(ids[0], message, state);
        }

        /// <summary>
        /// Handles the event when the server is broadcasting the presence of the contacts.
        /// </summary>
        /// <param name="sender">the object that triggered this event.</param>
        /// <param name="presence">The presence object that is passed back</param>
        private void OnPresenceHandler(object sender, Presence presence)
        {
            string id = presence.From.ToString().Split(new char[] { '/' })[0]; ;
            if (presence.Type.ToString().ToLower().Equals("unavailable"))
            {
                _Roster.UpdateContact(id, ContactStatus.Offline);
            }
            else
            {
                _Roster.UpdateContact(id, ConvertStatus(presence.Show.ToString()));
            }
            
        }

        /// <summary>
        /// Handles the roster items. The server will trigger this event after the client
        /// had successfully logged in.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="item"></param>
        private void OnRosterItemHandler(object sender, RosterItem item)
        {
            string id = item.Jid.ToString();
            string name = item.Name;
            Contact contact = new Contact(_Roster, id, name);
            _Roster.Add(contact);
        }

        /// <summary>
        /// away -- The entity or resource is temporarily away. 
        /// chat -- The entity or resource is actively interested in chatting. 
        /// dnd -- The entity or resource is busy (dnd = "Do Not Disturb"). 
        /// xa -- The entity or resource is away for an extended period (xa = "eXtended Away"). 
        /// </summary>
        /// <param name="XmppStatus"></param>
        /// <returns></returns>
        public static ContactStatus ConvertStatus(string XmppStatus)
        {
            if (XmppStatus != null)
            {
                if (XmppStatus.ToLower().Equals("away"))
                {
                    return ContactStatus.Away;
                }
                else if (XmppStatus.ToLower().Equals("dnd"))
                {
                    return ContactStatus.Busy;
                }
                else if (XmppStatus.ToLower().Equals("xa"))
                {
                    return ContactStatus.Away;
                }
                else
                {
                    return ContactStatus.Available;
                }
            }
            else
            {
                return ContactStatus.Offline;
            }
        }


        public void SentTo(string toID, string message)
        {
            Message msg = new Message(toID, MessageType.chat, message);
            _Xmpp.Send(msg);
        }
        public Contact FindContact(string contact)
        {
            return _Roster.FindContact(contact);
        }
    }

}
